﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using UCL.Powersource.Analysis;

namespace UCL.Powersource.Statistics
{
    /// <summary>
    /// Links related work item statistics base class
    /// </summary>
    [Export(typeof(IStatistic))]
    [DataContract(IsReference = true)]
    public abstract class LinksRelatedWIStatisticsBase : StatisticTeamBase
    {
        protected Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemCollection>>>> collectionCache;
        protected Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemLinkInfo[]>>>> linksCollectionCache;
        protected ILog Log;

        protected Dictionary<string, Object> variables;
        protected WorkItemLinkInfo[] queryLinkResults;
        protected WorkItemCollection queryResults;

        public LinksRelatedWIStatisticsBase()
            : base()
        {
            Log = LogManager.GetLog(typeof(WIStateStatisticsBase));
        }

        [DataMember]
        private string _FormatString = "0";

        public override string FormatString
        {
            get
            {
                return _FormatString;
            }
        }

        /// <summary>
        /// Class for tfs stat project configuration and work item time frame calculation
        /// </summary>
        /// <param name="teamProjConfig">Tfs stat project configuration</param>
        /// <param name="timeFrame">Work item time frame</param>
        /// <returns>Number of tfs stat project configuration, work item time frame</returns>
        protected override double? CalculateFor(ITfsTeamProjectConfiguration teamProjConfig, ITimeFrame timeFrame)
        {
            if (collectionCache == null) collectionCache = new Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemCollection>>>>();
            if (linksCollectionCache == null) linksCollectionCache = new Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemLinkInfo[]>>>>();
            return CalculateStatistic(teamProjConfig, timeFrame);
        }

        /// <summary>
        /// Class for statistics calculation
        /// </summary>
        /// <param name="teamProjConfig">Tfs stat project configuration</param>
        /// <param name="timeFrame">Work item time frame</param>
        /// <returns>Number of statistics</returns>
        protected double? CalculateStatistic(ITfsTeamProjectConfiguration teamProjConfig, ITimeFrame timeFrame)
        {
            ITimeFrameIteration iterationTimeFrame = (ITimeFrameIteration)timeFrame;
            if (iterationTimeFrame.FinishDate == null || iterationTimeFrame.StartDate == null) return null;

            string colUri = teamProjConfig.TeamProjectCollectionConfiguration.Uri;
            DateTime FinishDate = (DateTime)iterationTimeFrame.FinishDate;

            var WIStore = StatisticsUtilities.GetWIStore(teamProjConfig);

            string iteration = iterationTimeFrame.GetIterationPathForProject(teamProjConfig);
            if (variables == null)
            {
                variables = new Dictionary<string, object>();
                variables.Add("AsOf", null);
                variables.Add("IterationPath", null);
            }
            DateTime AsOf = StatisticsUtilities.GetAsOfDate(iterationTimeFrame);
            variables["AsOf"] = AsOf;
            variables["IterationPath"] =  iteration;
            
            return CountInvalidWI(colUri, iteration, FinishDate, WIStore);
        }

        /// <summary>
        /// Class for invalid work item calculation
        /// </summary>
        /// <param name="collUri">Tfs stat project collection Uri</param>
        /// <param name="iteration">ork item iteration</param>
        /// <param name="finishDate">Finish date</param>
        /// <param name="wiStore">Work item store</param>
        /// <returns>Number of in valid work items</returns>
        protected abstract int? CountInvalidWI(string collUri, string iteration, DateTime finishDate, WorkItemStore wiStore);

        /// <summary>
        /// Class for work items query
        /// </summary>
        /// <param name="queryString"></param>
        /// <param name="collUri">tfs stat project collection Uri</param>
        /// <param name="finishDate">Finsih date</param>
        /// <param name="ipath">Work item iteration path</param>
        /// <param name="wiStore">Work item store</param>
        /// <param name="qid"></param>
        /// <returns>Results of work items query</returns>
        protected WorkItemCollection GetQueryResults(string queryString, string collUri, DateTime finishDate, string ipath, WorkItemStore wiStore, string qid)
        {
            WorkItemCollection results;
            if (collectionCache == null) collectionCache = new Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemCollection>>>>();
            if (collectionCache[collUri][ipath][qid].ContainsKey(finishDate) &&
                collectionCache[collUri][ipath][qid][finishDate].Count != 0)
            {
                results = collectionCache[collUri][ipath][qid][finishDate];
            }
            else
            {
                try
                {
                    Query query = new Query(wiStore, queryString, variables);
                    results = query.RunQuery();
                }
                catch (ValidationException exc)
                {
                    Log.Error(exc, "Invalid arguments for query in GetQueryResults(), for collection {0} .", collUri);
                    return null;
                }
            }
                if (results != null)
                {
                    if (!collectionCache[collUri][ipath][qid].ContainsKey(finishDate))
                        collectionCache[collUri][ipath][qid].Add(finishDate, results);
                    else
                        collectionCache[collUri][ipath][qid][finishDate] = results;
                }
            
            return results;
        }

        /// <summary>
        /// Get the query link results class
        /// </summary>
        /// <param name="queryString">Work item query string</param>
        /// <param name="collUri">tfs stat project collection Uri</param>
        /// <param name="finishDate">finish date</param>
        /// <param name="ipath">Iteration path</param>
        /// <param name="wiStore">Work items store</param>
        /// <param name="qid"></param>
        /// <returns>Results of work item link query</returns>
        protected WorkItemLinkInfo[] GetQueryLinkResults(string queryString, string collUri, DateTime finishDate, string ipath, WorkItemStore wiStore, string qid)
        {
            WorkItemLinkInfo[] results;
            if (linksCollectionCache == null) linksCollectionCache = new Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemLinkInfo[]>>>>();
            if (linksCollectionCache[collUri][ipath][qid].ContainsKey(finishDate) &&
                linksCollectionCache[collUri][ipath][qid][finishDate].Count() != 0)
            {
                results = linksCollectionCache[collUri][ipath][qid][finishDate];
            }
            else
            {
                try
                {
                    Query query = new Query(wiStore, queryString, variables);
                    results = query.RunLinkQuery();
                }
                catch (ValidationException exc)
                {
                    Log.Error(exc, "Invalid arguments for query in GetLinkQueryResults(), for collection {0} .", collUri);
                    
                    return null;
                }
            }
                if (results != null)
                {
                    if (!linksCollectionCache[collUri][ipath][qid].ContainsKey(finishDate))
                        linksCollectionCache[collUri][ipath][qid].Add(finishDate, results);
                    else
                        linksCollectionCache[collUri][ipath][qid][finishDate] = results;
                }
            
            return results;
        }

        /// <summary>
        /// Class fot getting the list of Ids
        /// </summary>
        /// <param name="isPBI">Product backlog item</param>
        /// <param name="isLink">Work item link</param>
        /// <param name="wiStore">Work item store</param>
        /// <param name="ipath">Iteration path</param>
        /// <returns>List of Ids</returns>
        protected List<int> GetListOfIds(bool isPBI, bool isLink, WorkItemStore wiStore, string ipath)
        {
            return GetListOfIds(isPBI, isLink, true, wiStore, ipath);
        }

        /// <summary>
        /// Class for getting the list of Ids
        /// </summary>
        /// <param name="isPBI">Product Backlog Items</param>
        /// <param name="isLink">Work items link</param>
        /// <param name="retrieveOnlyRootElements">Retrieve onky root elements</param>
        /// <param name="wiStore">Work item store</param>
        /// <param name="ipath">WOrk item iterastin path</param>
        /// <returns>List of Ids</returns>
        protected List<int> GetListOfIds(bool isPBI, bool isLink,
            bool retrieveOnlyRootElements, WorkItemStore wiStore, string ipath)
        {
            string qString;
            List<int> Ids = new List<int>();
            int id;
            string ip;
            List<int> temp = new List<int>();
            string str = "''";
            List<WorkItem> results=new List<WorkItem>();
            WorkItemCollection qresults = null;

            if (isLink)
            {
            try {
                foreach (WorkItemLinkInfo link in queryLinkResults)
                {
                    if (link.SourceId != 0 && retrieveOnlyRootElements) continue;

                    temp.Add(link.TargetId);
                }

                if (isPBI)
                {
                    qresults = StatisticsUtilities.GetPBIs(wiStore, variables, retrieveOnlyRootElements);
                    foreach (WorkItem item in qresults)
                    {
                        if (temp.Contains(item.Id))
                            results.Add(item);
                    }
                }
                else
                {
                str = StatisticsUtilities.Format(temp);

                qString = "Select [ID], [Iteration Path] From WorkItem Where [ID] IN ("+str+") AsOf @AsOf";

                try
                {
                    Query query = new Query(wiStore, qString, variables);
                    qresults = query.RunQuery();
                }
                catch (ValidationException exc) 
                {
                    Log.Error(exc, "Invalid argument in query string in GetListOfIds() method.");

                }
                foreach (WorkItem item in qresults)
                {
                    results.Add(item);
                }
  
                }
            }
                catch (NullReferenceException exc)
                {
                    Log.Error(exc, "Null value for results from a link query in GetListOfIds() method.");
                }
              
            }
            else
            {
                if (isPBI)
                {
                    qresults = StatisticsUtilities.GetPBIs(wiStore, variables, retrieveOnlyRootElements);
                    foreach (WorkItem item in queryResults)
                    {
                        if (qresults.Contains(item)) results.Add(item);
                    }
                }
                else
                {
                    qresults = queryResults;
                    foreach (WorkItem item in qresults)
                    {
                        results.Add(item);
                    }
                }
            }
          
                try
                {
                    foreach (WorkItem item in results)
                    {
                        id = (int)item.Fields["Id"].Value;
                        ip = (string)item.Fields["Iteration Path"].Value;

                        if (isPBI)
                        { if (!Ids.Contains(id)) Ids.Add(id); }
                        else
                            if (ip.Contains(ipath) && !Ids.Contains(id))
                                Ids.Add(id);
                    }
                }
                catch (NullReferenceException exc)
                {
                    Log.Error(exc, "Null value for results from a query in GetListOfIds() method.");
                }
        
            return Ids;
        }
        public override bool RequiresBuild
        {
            get { return false; }
        }
    }
}
